/**
 * 文件名称: socket.c
 * 摘    要: 与socket相关操作源文件
 * 来    源: 自我总结
 *
 * 当前版本: 1.0 
 * 作    者: huenrong
 * 完成日期: 2019-07-07
 **/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <errno.h>
#include <pthread.h>

#include "socket.h"
#include "../write_log/write_log.h"


// 服务器线程传参结构体
struct server_thread_parameter
{
    int (* server_function)(const int *);       // 服务器功能函数
    int server_port;        // 服务器监听端口号
};

/************************************************************************
函数名称: tcp_server_thread
函数功能: 创建tcp server套接字并创建tcp server功能函数线程
函数参数: tcp_server_thread_parameter: tcp server 监听串口号
函数返回: 无
************************************************************************/
static int tcp_server_thread(const struct server_thread_parameter *tcp_server_thread_parameter)
{
    int ret = -1;
    int server_fd = -1;     // 服务器socket套接字
    struct sockaddr_in server_addr;     // 服务器socket地址
    int server_port = 0;        // 服务器监听port
    char *err_msg = NULL;       // 记录出错信息

    // 客户端套接字
    int client_fd = -1;     // 客户端套接字
    char client_ip[INET_ADDRSTRLEN] = {0};   // 用于保存客户端IP地址
    struct sockaddr_in client_addr;         // 用于保存客户端地址
    socklen_t client_addr_len = sizeof(client_addr);        // 必须初始化!!!

    pthread_t recv_file_function_thread_id = -1;        // cp server功能函数线程ID
    unsigned char fail_num = 0;     // 创建线程失败次数

    server_port = tcp_server_thread_parameter->server_port;

    // 创建TCP套接字
    // SOCK_STREAM表示使用面向连接的socket，即使用TCP连接
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0)
	{
    #ifdef DEBUG
		perror("tcp_server_thread socket error");
    #endif
        err_msg = NULL;
        err_msg = calloc(strlen("tcp_server_thread socket error: ") + 
                            strlen(strerror(errno)) + 1, 1);
        sprintf(err_msg, "tcp_server_thread socket error: %s", strerror(errno));
        WRITE_LOG_FILE(LOG_FATAL, err_msg);
        free(err_msg);

		exit(-1);
	}

    // 初始化服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;       // 协议族
    server_addr.sin_port = htons(server_port);      // 服务器监听port
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);        // INADDR_ANY表示任何IP地址, 也可指定本机地址
#ifdef DEBUG
    printf("file_transfer_server listening port: [%d]...\n", server_port);
#endif
    err_msg = NULL;
    err_msg = calloc(strlen("file_transfer_server listening port: [%d]...") + 
                        5 + 1, 1);
    sprintf(err_msg, "file_transfer_server listening port: [%d]...", server_port);
    WRITE_LOG_FILE(LOG_INFO, err_msg);
    free(err_msg);

    // 绑定
    ret = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (0 != ret)
    {
    #ifdef DEBUG
        perror("tcp_server_thread bind error");
    #endif
        err_msg = NULL;
        err_msg = calloc(strlen("tcp_server_thread bind error: ") + 
                            strlen(strerror(errno)) + 1, 1);
        sprintf(err_msg, "tcp_server_thread bind error: %s", strerror(errno));
        WRITE_LOG_FILE(LOG_FATAL, err_msg);
        free(err_msg);

		close(server_fd);
        exit(-1);
    }

    // 监听
    // 10 表示：等待连接队列的最大长度
    ret = listen(server_fd, 10);
    if (0 != ret)
    {
    #ifdef DEBUG
        perror("tcp_server_thread listen error");
    #endif
        err_msg = NULL;
        err_msg = calloc(strlen("tcp_server_thread listen error: ") + 
                            strlen(strerror(errno)) + 1, 1);
        sprintf(err_msg, "tcp_server_thread listen error: %s", strerror(errno));
        WRITE_LOG_FILE(LOG_FATAL, err_msg);
        free(err_msg);

		close(server_fd);
        exit(-1);
    }
#ifdef DEBUG
    printf("file_transfer_server start, waiting for connection...\n");
#endif
    err_msg = NULL;
    err_msg = calloc(strlen("file_transfer_server start, waiting for connection...") + 1, 1);
    sprintf(err_msg, "file_transfer_server start, waiting for connection...");
    WRITE_LOG_FILE(LOG_INFO, err_msg);
    free(err_msg);

    while (1)
    {
        // 获得一个已经建立的连接, 会阻塞进程，直到有客户端连接上来为止
        client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_addr_len);
        if (client_fd < 0)
        {
        #ifdef DEBUG
            perror("tcp_server_thread accept error");
        #endif
            err_msg = NULL;
            err_msg = calloc(strlen("tcp_server_thread accept error: ") + 
                                strlen(strerror(errno)) + 1, 1);
            sprintf(err_msg, "tcp_server_thread accept error: %s", strerror(errno));
            WRITE_LOG_FILE(LOG_FATAL, err_msg);
            free(err_msg);

            continue;       // 获取连接失败，则重新获取
        }

        // 打印连接到服务器的客户端IP和port
        inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
    #ifdef DEBUG
        printf("-------------------------------------------------------\n");
        printf("file_transfer_server accept new connection from [%s:%d]\n", 
                client_ip, ntohs(client_addr.sin_port));
    #endif
        err_msg = NULL;
        err_msg = calloc(strlen("file_transfer_server accept new connection from ") + 
                            strlen(client_ip) + 5 + 5, 1);
        sprintf(err_msg, "file_transfer_server accept new connection from [%s:%d]", 
                client_ip, ntohs(client_addr.sin_port));
        WRITE_LOG_FILE(LOG_INFO, "-------------------------------------------------------");
        WRITE_LOG_FILE(LOG_INFO, err_msg);
        free(err_msg);

        // 连接上一个客户端就创建一个线程处理函数
        ret = pthread_create(&recv_file_function_thread_id, NULL, \
                             (void *)tcp_server_thread_parameter->server_function, &client_fd);
        if (0 != ret)
        {
        #ifdef DEBUG
            perror("tcp_server_thread pthread_create fail!\n");
        #endif
            err_msg = NULL;
            err_msg = calloc(strlen("tcp_server_thread pthread_create fail") + 1, 1);
            sprintf(err_msg, "tcp_server_thread pthread_create fail");
            WRITE_LOG_FILE(LOG_FATAL, err_msg);
            free(err_msg);

            usleep(50 * 1000);
            fail_num++;
            if (3 == fail_num)      // 创建3次都失败则认为失败
            {
                return -1;
            }
        }
        else if (0 == ret)      // 线程创建成功，分离线程，并退出
        {
            pthread_detach(recv_file_function_thread_id);      // 线程分离，结束时自动回收资源

            //return 0;     // 这里不返回，否则不能接收多个客户端
        }
    }
}

/************************************************************************
函数名称: creat_tcp_server
函数功能: 创建tcp server线程
函数参数: recv_file_function: tcp server的功能函数
          tcp_server_port: tcp server 监听串口号
函数返回: 成功: 返回0
          失败: 返回-1
************************************************************************/
int creat_tcp_server(int (* recv_file_function)(const int *), const int tcp_server_port)
{
    int ret = -1;
    unsigned char fail_num = 0;     // 创建线程失败次数
    pthread_t tcp_server_thread_id = -1;        // tcp server线程ID
    char *err_msg = NULL;       // 记录出错信息
    
    struct server_thread_parameter tcp_server_thread_parameter = {0};

    tcp_server_thread_parameter.server_function = recv_file_function;
    tcp_server_thread_parameter.server_port = tcp_server_port;

    while (1)
    {
        // 创建tcp server线程
        ret = pthread_create(&tcp_server_thread_id, NULL, \
                             (void *)tcp_server_thread, (void *)&tcp_server_thread_parameter);
        if (0 != ret)
        {
        #ifdef DEBUG
            perror("creat tcp_server_thread fail!\n");
        #endif
            err_msg = NULL;
            err_msg = calloc(strlen("tcp_server_thread accept error: ") + 
                                strlen(strerror(errno)) + 1, 1);
            sprintf(err_msg, "tcp_server_thread accept error: %s", strerror(errno));
            WRITE_LOG_FILE(LOG_FATAL, err_msg);
            free(err_msg);
            
            usleep(50 * 1000);
            fail_num++;
            if (3 == fail_num)      // 创建3次都失败则认为失败
            {
                return -1;
            }
        }
        else if (0 == ret)      // 线程创建成功，分离线程，并退出
        {
            pthread_detach(tcp_server_thread_id);       // 线程分离，结束时自动回收资源
            
            return 0;
        }
    }
}


